package xj.meta;

import java.io.PrintWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.WeakHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import xj.util.xml.XMLUtil;

public class ModelRepository {

  public static ModelRepository getInstance() {
    if (theInstance == null) {
      theInstance = new ModelRepository();
    }
    return theInstance;
  }

  public static void setUseWeakReferences(boolean b) { 
    useWeakReferences = b;
  }

  public static void setTransientMode(boolean b) { 
    transientMode = b;
  }

  public static boolean isTransientMode() { 
    return transientMode;
  }

  public ModelElement getRoot() {
    return root;
  }

  public void setRoot(ModelElement root) {
    this.root = root;
    addElement(root);
  }

  public void addElement(ModelElement e) {
    if (e != null) {
      elements.put(e.getId(), e);
    }
  }

  public void addAllElements(Collection<ModelElement> c) {
    if (c != null) {
      Iterator<ModelElement> iter = c.iterator();
      while (iter.hasNext()) {
	ModelElement e = iter.next();
	if (e != null) {
	  elements.put(e.getId(), e);
	}
      }
    }
  }

  public void removeElement(String id) {
    if (id != null) {
      elements.remove(id);
    }
  }

  public void removeElement(ModelElement e) {
    if (e != null) {
      elements.remove(e.getId());
    }
  }

  public ModelElement getElement(String id) {
    if (id != null) {
      return elements.get(id);
    }
    return null;
  }

  public void addModel(RootElement p) {
    if (p != null) {
      String name = p.getName();
      if (name != null) {
	models.put(name, p);
      }
    }
  }

  public RootElement getModel(String name) {
    if (name != null) {
      return models.get(name);
    }
    return null;
  }

  public void addRelation(String rname) {
    if (!relations.containsKey(rname)) {
      relations.put(rname, new ModelRelation(rname));
    }
  }

  public void removeRelation(String rname) {
    if (relations.containsKey(rname)) {
      relations.remove(rname);
    }
  }

  public ModelRelation getRelation(String rname) {
    return relations.get(rname);
  }

  public Set<String> getRelationNames() {
    return relations.keySet();
  }

  public void addAssociationElement(String rname, AssociationElement assoc) {
    if (rname != null && assoc != null) {
      ModelRelation r = relations.get(rname);
      if (r != null) {
	r.add(assoc);
      }
    }
  }

  public void addAssociationElement(String rname, ModelElement x, ModelElement y) {
    if (rname != null && x != null && y != null) {
      ModelRelation r = relations.get(rname);
      if (r != null) {
	r.add(x, y);
      }
    }
  }

  public Iterator<ModelElement> elementIterator() {
    return elements.values().iterator();
  }

  public Iterator<ModelRelation> relationIterator() {
    return relations.values().iterator();
  }

  public void resolveLinks() {
    Iterator<ModelElement> iter = elementIterator();
    while (iter.hasNext()) {
      ModelElement e = iter.next();
      if (e instanceof ModelReference) {
	ModelReference ref = (ModelReference) e;
	ref.resolveLink();
      }
    }
  }

  public void clearRepository() {
    elements.clear();
    relations.clear();
    root = null;
  }

  public void setNamespace(String namespaceURI, String namespacePrefix) {
    this.namespaceURI = namespaceURI;
    this.namespacePrefix = namespacePrefix;
  }

  public String getNamespaceURI() {
    return namespaceURI;
  }

  public String getNamespacePrefix() {
    return namespacePrefix;
  }

  public boolean isStrictNamespace() {
    return strictNamespace;
  }

  public void setStrictNamespace(boolean strictNamespace) {
    this.strictNamespace = strictNamespace;
  }

  public void exportXML(PrintWriter out) {
    exportXML(out, root);
  }

  public void exportXML(PrintWriter out, ModelElement eroot) {
    if (out != null && eroot != null) {
      out.println("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>");
      eroot.exportXML(out, 0);
      out.flush();
    }
  }

  public void exportXMI(PrintWriter out) {
    exportXMI(out, root);
  }

  public void exportXMI(PrintWriter out, ModelElement eroot) {
    // xj.meta.mdr.XMIExporter xmi = new xj.meta.mdr.XMIExporter();
    /*
     * xj.meta.xmi.XMIExporter xmi = new xj.meta.xmi.XMIExporter();
     * xmi.export(out, eroot); out.flush();
     */
  }

  public ModelElement loadXMLFile(String filename) {
    if (debug) {
      System.out.println("loadXMLFile(): filename=" + filename);
    }

    clearRepository();

    org.w3c.dom.Document xmldoc = XMLUtil.createXMLDocument(filename, false);
    if (xmldoc != null) {
      if (debug) {
	System.out.println("loadXMLFile(): create XML document success");
      }

      org.w3c.dom.Element docElement = xmldoc.getDocumentElement();
      ModelElement root = ModelBuilder.loadElementFromXML(docElement);
      ModelRepository rep = ModelRepository.getInstance();
      if (root instanceof RootElement) {
	rep.addModel((RootElement) root);
      }
      rep.resolveLinks();
      return root;
    }
    return null;
  }

  public void dump() {
    System.out.println("==== ModelRepository dump begin ====");
    System.out.println("==== ModelRepository model elements begin ====");
    Collection<ModelElement> c = elements.values();
    Iterator<ModelElement> iter = c.iterator();
    int i = 0;
    while (iter.hasNext()) {
      ModelElement e = iter.next();
      if (e != null) {
	System.out.println("[" + i + "]: " + e.getUUID() + " " + e.getId() + " " + e.getElementName());
	System.out.println("\t" + ((AbstractModelElement) e).ownedElementsString());
	i++;
      }
    }
    System.out.println("==== ModelRepository model elements end ====");
    System.out.println("==== ModelRepository model elements map begin ====");
    Set<Map.Entry<String, ModelElement>> eset = elements.entrySet();
    Iterator<Map.Entry<String, ModelElement>> eiter = eset.iterator();
    while (eiter.hasNext()) {
      Map.Entry<String, ModelElement> entry = eiter.next();
      if (entry != null) {
	ModelElement e = entry.getValue();
	System.out.println("[" + entry.getKey() + "]: " + " " + e.getId() + " " + e.getElementName());
      }
    }
    System.out.println("==== ModelRepository model elements map end ====");

    System.out.println("==== ModelRepository model relation begin ====");
    Iterator<ModelRelation> riter = relationIterator();
    i = 0;
    while (riter.hasNext()) {
      ModelRelation r = riter.next();
      System.out.println("[" + i + "]: " + r.getUUID() + " " + r.getId() + " " + r.getName());
      Iterator<AssociationElement> piter = r.iterator();
      while (piter.hasNext()) {
	AssociationElement p = piter.next();
	System.out.println("\t(" + p.getLeftElement().getId() + ", "  + p.getRightElement().getId() + ")");
      }
      i++;
    }
    System.out.println("==== ModelRepository model relation end ====");

    System.out.println("==== ModelRepository dump end ====");
  }

  protected ModelRepository() {
    if (useWeakReferences) { 
      models = new HashMap<String, RootElement>();
      elements = new HashMap<String, ModelElement>();
      relations = new HashMap<String, ModelRelation>();
    } else { 
      models = new WeakHashMap<String, RootElement>();
      elements = new WeakHashMap<String, ModelElement>();
      relations = new WeakHashMap<String, ModelRelation>();
    }
  }

  protected static boolean useWeakReferences = false; 

  protected static boolean transientMode = false; 

  protected static ModelRepository theInstance = null;

  protected ModelElement root;

  protected Map<String, RootElement> models;

  protected Map<String, ModelElement> elements;

  protected Map<String, ModelRelation> relations;

  protected String namespaceURI = null;

  protected String namespacePrefix = null;

  protected boolean strictNamespace = false;

  protected static boolean useDefaultNamespace = true;

  protected static final boolean debug = true;

}
